import { _decorator, Component, Node, Vec3, Quat, lerp, clamp01, Vec2, Mat4, geometry, PhysicsSystem, Camera, find } from 'cc';
// import { DebugDraw } from '../../utils/debugDraw';
const { ccclass, property, executeInEditMode } = _decorator;

// 临时变量
let _tempQuat: Quat = new Quat;
let _tempVec3: Vec3 = new Vec3;
let _tempVec3_2: Vec3 = new Vec3;
let _forward: Vec3 = new Vec3(0, 0, 1);
let _matView: Mat4 = new Mat4;
let _tempRay: geometry.Ray = new geometry.Ray;

export interface CameraVector {
    forward: Vec3,
    up: Vec3,
    right: Vec3,
};

/**
 * 摄像机同步信息
 */
export interface SyncCameraInfo {
    follow?: Node, targetOffset?: Vec3, targetRadius?: number, targetRotation?: Vec3, lookOffset?: Vec3, fov?: number, minPitch?: number
}

// 跟随摄像机
@ccclass('FollowCamera')
@executeInEditMode
export class FollowCamera extends Component {
    private _follow: Node | null = null;
    public _camera: Camera | null = null;

    // 摄像机的跟随目标
    @property({ tooltip: '跟随节点' })
    set follow (value: Node | null) {
        this._follow = value;
        // if (!this._follow) return;
        // this._follow.getWorldPosition(_tempVec3);
        // Vec3.copy(this._followPosition, _tempVec3);
        // this.node.getWorldPosition(_tempVec3_2);
        // _tempVec3.subtract(_tempVec3_2);
        // this.targetRadius = _tempVec3.length();
        // this._followRadius = this.targetRadius;
    }
    get follow () {
        return this._follow;
    }

    @property({ tooltip: '设置朝向目标的角度' })         /// 设置朝向目标的角度
    set targetRotation (value: Vec3) {
        this._targetRotation = value;
    }

    get targetRotation () {
        return this._targetRotation;
    }

    @property({ tooltip: '摄像机的目标偏移' })
    targetOffset: Vec3 = new Vec3;

    @property({ tooltip: '摄像机的最终视角偏移' })
    lookOffset: Vec3 = new Vec3;

    @property({ tooltip: '是否启用视线遮挡' })
    sightOcclusion: boolean = false;

    @property({ tooltip: '修正视线遮挡穿模', visible (this: FollowCamera) { return this.sightOcclusion } })
    fixedSightOcclusion: boolean = true;
    // 修复穿帮摄像机需要达到的高度
    @property({ tooltip: '修复高度', visible (this: FollowCamera) { return this.sightOcclusion && this.fixedSightOcclusion } })
    useFixedColliderHeight: number = 1.85;

    @property({ tooltip: '旋转速度' })
    rotateSpeed = 7;
    @property({ tooltip: '跟随速度' })
    followSpeed = 5;
    @property({ tooltip: '距离切换速度' })
    radiusSpeed = 10;

    // 摄像头操作相关参数
    @property
    rotateXEnable: boolean = false;      // 是否允许x轴旋转

    @property
    rotateYEnable: boolean = false;     // 是否允许y轴旋转

    @property
    rotateFactor: number = 0.05;        // 摄像机移动速度

    @property({ tooltip: '限制摄像机到目标距离' })
    public limitRadius: boolean = false;

    @property({ visible (this: FollowCamera) { return this.limitRadius } })
    minRadius: number = 0;              // 摄像机距离目标最小距离

    @property({ visible (this: FollowCamera) { return this.limitRadius } })
    maxRadius: number = 500;            // 摄像机距离目标最大距离

    @property({ tooltip: '限制摄像机上下视角' })
    public limitPitch: boolean = false;

    @property({ visible (this: FollowCamera) { return this.limitPitch } })
    minPitch: number = -60;         // 摄像机pitch最小值

    @property({ visible (this: FollowCamera) { return this.limitPitch } })
    maxPitch: number = 30;         // 摄像机pitch最大值 

    @property({ tooltip: '限制摄像机左右视角' })
    limitYaw: boolean = false;

    @property({ visible (this: FollowCamera) { return this.limitYaw } })
    minYaw: number = -45;         // 摄像机yaw最小值 

    @property({
        visible (this: FollowCamera) {
            return this.limitYaw;
        },
    })
    maxYaw: number = 45;         // 摄像机yaw最大值 

    protected _targetRotation: Vec3 = new Vec3;

    protected _targetRadius: number = 4;
    protected _originRadius: number = 4;
    protected _originPosition: Vec3 = new Vec3;

    protected _offsetVec: Vec3 = new Vec3;
    protected _followPosition: Vec3 = new Vec3;
    protected _currentPositon: Vec3 = new Vec3;
    protected _followRotation: Quat = new Quat;
    protected _followRadius: number = 0;
    protected _targetPosition: Vec3 = new Vec3;
    protected _occlusionRadius: number = -1;


    public worldRight: Vec3 = new Vec3;
    public worldUp: Vec3 = new Vec3;
    public worldDirection: Vec3 = new Vec3;

    private _forwardVector: Vec3 = new Vec3;     // 摄像机向前的向量
    private _rightVector: Vec3 = new Vec3;       // 摄像机向右的向量
    private _upVector: Vec3 = new Vec3;          // 摄像机向上的向量

    private _primary: Vec2 | null = null;
    private _scroll: Vec2 | null = null;

    @property
    public set targetRadius (value: number) {
        // // 当被墙遮挡时，可以滚动到很远的地方，但可以滚动到目标附近
        // if (this._occlusionRadius > 0 && this._occlusionRadius < value) {
        //     //下一次的时间值必须小于闭塞半径
        //     if (value < this._originRadius) {
        //         this._originRadius = this._occlusionRadius - (this._originRadius - value);
        //         return;
        //     }
        //     return;
        // }
        this._originRadius = value;
    }

    public get targetRadius () {
        return this._originRadius;
    }

    // @property
    // public debugFollowPoint: boolean = false;


    onLoad () {
        this._camera = this.node.getComponent(Camera);
        if (this._camera == null) {
            console.warn("_camera == null");
            return;
        }
        this._camera.near = 0.1;
    }

    start () {
        // setTimeout(() => {
        //     let node = find("virtualCameraNode");
        //     if (node) {
        //         this.syncVirtualCameraNode(node, 0);
        //     }
        // }, 10 * 1000);
        // Vec3.copy(this._followPosition, this.node.position);
        // this.node.getWorldRotation(this._followRotation);
        // Quat.toEuler(this._targetRotation, this._followRotation);
    }

    update () {
        if (this._primary && this._primary.length() > 0) {
            this.rotationCamera(this._primary);
        }
        if (this._scroll && this._scroll.y != 0) {
            this.updateRadiuseDelta(this._scroll.y);
        }
    }

    // 绑定输入
    public bindInput (primary: Vec2 | null, scroll: Vec2 | null) {
        this._primary = primary;
        this._scroll = scroll;
    }

    /**
     * 旋转摄像机
     * @param delta 摄像机旋转量
     */
    public rotationCamera (delta: Vec2) {
        if (!this.rotateXEnable && !this.rotateYEnable) {
            return;
        }
        if (this.rotateXEnable) {
            this._targetRotation.y -= delta.x * this.rotateFactor;
        }

        if (this.rotateYEnable) {
            this._targetRotation.x -= delta.y * this.rotateFactor;
        }
        this._limitRotation(this._targetRotation);
    }

    private _limitRotation (rotation: Vec3) {
        if (this.limitYaw) {
            if (rotation.x < this.minPitch) {
                rotation.x = this.minPitch;
            } else if (rotation.x > this.maxPitch) {
                rotation.x = this.maxPitch;
            }
        }

        if (this.limitYaw && this.follow) {
            let curYaw = rotation.y - 180 - this.follow.eulerAngles.y;
            if (curYaw < this.minYaw) {
                rotation.y = this.minYaw + 180 + this.follow.eulerAngles.y;
            } else if (curYaw > this.maxYaw) {
                rotation.y = this.maxYaw + 180 + this.follow.eulerAngles.y;
            }
        }

        rotation.z = 0;
    }

    // onEnable () {
    //     ClientEvent.on(Constant.EVENT.SWITCH_MAIN_FOLLOW_CAMERA, this.changeMainCamera, this);
    // }

    // onDisable () {
    //     ClientEvent.off(Constant.EVENT.SWITCH_MAIN_FOLLOW_CAMERA, this.changeMainCamera, this);
    // }

    /**
     * 更新摄像机距离
     * @param value 
     */
    public updateRadiuseDelta (value: number) {
        let targetRadius = this._originRadius;
        targetRadius += -Math.sign(value);
        targetRadius = Math.min(this.maxRadius, Math.max(this.minRadius, targetRadius));
        this._originRadius = targetRadius;
    }

    lateUpdate (dt: number) {
        if (!this._follow) return;

        // 摄像机运动基本流程如下
        // 获取跟随节点的坐标->进行目标点偏移->平滑旋转摄像机到新角度->应用视角偏移->获得跟随节点偏移后位置
        // 计算摄像机距离跟随节点的最长距离->计算出摄像机世界朝向->根据最长距离跟朝向以及跟随节点偏移后位置计算出最终摄像机坐标点

        // 获取跟随点的坐标，根据目标朝向计算出偏移后的最终目标点
        this._follow.getWorldPosition(this._targetPosition);
        this._follow.getWorldRotation(_tempQuat);
        Vec3.transformQuat(_tempVec3, this.targetOffset, _tempQuat);
        Vec3.add(this._targetPosition, this._targetPosition, _tempVec3);

        // 跟随的旋转角度由目标角度进行平滑移动
        Quat.fromEuler(_tempQuat, this._targetRotation.x, this._targetRotation.y, this._targetRotation.z);
        Quat.slerp(this._followRotation, this._followRotation, _tempQuat, clamp01(dt * this.rotateSpeed));

        // 应用视角偏移
        Vec3.transformQuat(_tempVec3, this.lookOffset, this._followRotation);
        Vec3.add(this._targetPosition, this._targetPosition, _tempVec3);

        // 平滑过度当前摄像机位置到目标位置
        Vec3.lerp(this._followPosition, this._followPosition, this._targetPosition, clamp01(dt * this.followSpeed));

        // 目标方向根据旋转角度计算出当前摄像机相对于世界坐标系的朝向
        Vec3.transformQuat(this.worldDirection, _forward, this._followRotation);

        // if (this.debugFollowPoint) {
        //     DebugDraw.instance.sphere({ position: this._followPosition, scale: 0.1 }, 1, "FollowPoint");
        // }

        // 根据遮挡计算摄像机偏离目标节点最长距离
        if (this.sightOcclusion) {
            this._targetRadius = this._handleIfSightOcclusion(this._followPosition);
        }
        //this._targetRadius = this._originRadius;

        // 平滑过度摄像机距离跟随节点的最长距离
        this._followRadius = lerp(this._followRadius, this._targetRadius, clamp01(dt * this.radiusSpeed));

        // 根据摄像机方向和摄像机距离目标点最长距离计算摄像机坐标点距离跟随点的坐标
        Vec3.multiplyScalar(_tempVec3_2, this.worldDirection, this._followRadius);
        // 最终偏移坐标跟跟随最终跟随坐标相加得到摄像机最终坐标
        Vec3.add(this._currentPositon, this._followPosition, _tempVec3_2);

        // 应用额外高度防止穿透人物
        if (this.sightOcclusion && this.fixedSightOcclusion) {
            _tempVec3_2.set(0, (this.useFixedColliderHeight - this.targetOffset.y) * Math.max(1 - this._followRadius / (this._originRadius > 0 ? this._originRadius : 2), 0), 0);
            Vec3.add(this._currentPositon, this._currentPositon, _tempVec3_2);
        }

        // 设置摄像机坐标，以及旋转角度
        this.node.setWorldPosition(this._currentPositon);
        this.node.setWorldRotation(this._followRotation);
    }

    /**
     * 返回摄像机观察方向
     * @returns 
     */
    public getCameraDirection (out: Vec3) {
        this.node.getRotation(_tempQuat);
        Vec3.transformQuat(_tempVec3, new Vec3(0, 0, 1), _tempQuat);

        Vec3.copy(out, _tempVec3);
        out.multiplyScalar(-1);
    }

    /**
     * 返回摄像机各个朝向
     * @returns 
     */
    public getCameraVector (): CameraVector {
        // 获取摄像机朝向
        Mat4.invert(_matView, this.node.worldMatrix);

        this._rightVector.x = _matView.m00;
        this._rightVector.y = _matView.m04;
        this._rightVector.z = _matView.m08;

        this._upVector.x = _matView.m01;
        this._upVector.y = _matView.m05;
        this._upVector.z = _matView.m09;

        this._forwardVector.x = -_matView.m02;
        this._forwardVector.y = -_matView.m06;
        this._forwardVector.z = -_matView.m10;

        return { forward: this._forwardVector, up: this._upVector, right: this._rightVector }
    }

    /**
     * 限制镜头 Radius
     * @param followWorldPosition 
     * @returns 
     */
    private _handleIfSightOcclusion (followWorldPosition: Vec3) {
        geometry.Ray.set(_tempRay,
            followWorldPosition.x, followWorldPosition.y, followWorldPosition.z,
            this.worldDirection.x, this.worldDirection.y, this.worldDirection.z);

        //    DebugDraw.instance.ray({ ray: _tempRay }, "followCamera");
        let hasHit = PhysicsSystem.instance.raycast(_tempRay, 0xffffffff, this._originRadius);
        if (!hasHit) {
            return this._originRadius;
        }

        let hitResults = PhysicsSystem.instance.raycastResults;
        hitResults.sort((o1, o2) => {
            return o1.distance - o2.distance;
        });

        let distance = this._originRadius;
        for (let i = 0; i < hitResults.length; i++) {
            if (hitResults[i].collider.name.indexOf("wall") != -1
                //    ||hitResults[i].collider.name.indexOf("chair") != -1
            ) {
                continue;
            } else {
                distance = hitResults[i].distance;
                break;
            }
        }

        return distance;
    }

    /**
     * 同步摄像机参数
     * @param info 
     */
    public asyncFollowInfo (info: SyncCameraInfo) {
        if (info.hasOwnProperty("follow") && info.follow) {
            this.follow = info.follow;
        }
        if (info.hasOwnProperty("targetOffset") && info.targetOffset) {
            this.targetOffset = info.targetOffset;
        }
        if (info.hasOwnProperty("targetRadius") && info.targetRadius) {
            this.targetRadius = info.targetRadius;
        }
        if (info.hasOwnProperty("targetRotation") && info.targetRotation) {
            this.targetRotation = info.targetRotation;
        }

        if (info.hasOwnProperty("lookOffset") && info.lookOffset) {
            this.lookOffset = info.lookOffset;
        }

        if (info.hasOwnProperty("minPitch") && info.minPitch) {
            this.minPitch = info.minPitch;
        }

        if (info.hasOwnProperty("fov") && this._camera && info.fov) {
            this._camera.fov = info.fov;
        }
    }
}

